#pragma once
#include <queue>
#include <vector>
#include <string>
#include <bitset>
#include <random>
#include <memory>
#include <sstream>
#include <fstream>
#include <assert.h>
#include <algorithm>
#include <functional>
#include <filesystem>
#include <unordered_map>

#include <glad/glad.h>

#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/matrix_decompose.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <glm/ext/matrix_clip_space.hpp>

// include spdlog
#define FMT_HEADER_ONLY 
#define SPDLOG_FMT_EXTERNAL
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>

// import, export
#ifdef SHADER_EXPORT
    #ifdef _MSC_VER
        #define SHADER_API __declspec(dllexport)
    #else
        #define SHADER_API __attribute__((visibility("default")))
    #endif
#else
    #ifdef _MSC_VER
        #define SHADER_API __declspec(dllimport)
    #else
        #define SHADER_API
    #endif
#endif

// runtime assertion
#define SHADER_ASSERT assert

// compile assertion
#if defined(__clang__) || defined(__gcc__)
    #define SHADER_STATIC_ASSERT _Static_assert
#else
    #define SHADER_STATIC_ASSERT static_assert
#endif

// function inlining
#if defined(__clang__) || defined(__gcc__)
    #define SHADER_INLINE __attribute__((always_inline)) inline
    #define SHADER_NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER)
    #define SHADER_INLINE __forceinline
    #define SHADER_NOINLINE __declspec(noinline)
#else
    #define SHADER_INLINE inline
    #define SHADER_NOINLINE
#endif

// core features
namespace Shader
{
   
    // console logging
    struct SHADER_API Logger 
    { 
        using SPDLog = std::shared_ptr<spdlog::logger>;

        SHADER_INLINE Logger()
        {
            m_SPD = spdlog::stdout_color_mt("stdout");
            spdlog::set_level(spdlog::level::trace);              
            spdlog::set_pattern("%^[%T]: [#%t] %v%$");
        }

        SHADER_INLINE static SPDLog& Ref() 
        {
            static Logger logger;
            return logger.m_SPD;
        }   

    private:
        SPDLog m_SPD;         
    };
}

// logging macros

#ifdef SHADER_ENABLE_LOG
    #define SHADER_TRACE(...) Shader::Logger::Ref()->trace(__VA_ARGS__)
    #define SHADER_DEBUG(...) Shader::Logger::Ref()->debug(__VA_ARGS__)
    #define SHADER_INFO(...)  Shader::Logger::Ref()->info(__VA_ARGS__)
    #define SHADER_WARN(...)  Shader::Logger::Ref()->warn(__VA_ARGS__)
    #define SHADER_ERROR(...) Shader::Logger::Ref()->error(__VA_ARGS__)
    #define SHADER_FATAL(...) Shader::Logger::Ref()->critical(__VA_ARGS__) 
#else
	#define SHADER_TRACE
	#define SHADER_DEBUG
	#define SHADER_ERROR	
	#define SHADER_FATAL
	#define SHADER_INFO
    #define SHADER_WARN
#endif

// free allocated memory
#define SHADER_DELETE(ptr) if (ptr != nullptr) { delete (ptr); ptr = nullptr; }
